package proxy

import (
	"fmt"
	"crypto/tls"
	"net/url"
	"net/http"
	"time"
	"errors"
	"github.com/PuerkitoBio/goquery"
	"learn/smart/util/redis"
	"learn/smart/worker"
	"strconv"
	"strings"
)

var httpClient *http.Client

func init() {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	proxy, err := url.Parse("http://127.0.0.1:1080")

	if err != nil {
		fmt.Println(err)
		return
	}
	tr.Proxy = http.ProxyURL(proxy)
	httpClient = &http.Client{
		Transport: tr,
		Timeout:   time.Second * 10, //超时时间
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse //禁止跳转
		},
	}
}

func GetProxy() {
	inChan := make(chan worker.InProxy)
	TestAndAddRedis(inChan)
	for {
		CreateNova(inChan)
		<-time.Tick(10 * time.Minute)
	}
}

func CreateNova(inChan chan worker.InProxy) {
	resp, err := httpClient.Get("https://www.proxynova.com/proxy-server-list/country-cn/")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	doc.Find("tbody tr").Each(func(i int, s *goquery.Selection) {
		go func(i int, s *goquery.Selection) {
			ip := s.Find("td").Eq(0).Find("abbr").Eq(0).AttrOr("title", "")
			port := s.Find("td").Eq(1).Find("a").Eq(0).Text()
			protocol := "http"
			if ip != "" && port != "" {
				var in worker.InProxy
				in.Ip = ip
				in.Port = port
				in.Protocol = protocol
				inChan <- in
			}
		}(i, s)
	})
}

func CreateProxies(inChan chan worker.InProxy, page int) {
	fmt.Println(page)
	path := fmt.Sprintf(`/nn/%d`, page)
	resp, err := httpClient.Get(path)

	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	fmt.Println(resp.StatusCode)

	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		fmt.Println(err)
		return
	}
	doc.Find("tbody tr").Each(func(i int, s *goquery.Selection) {
		go func(i int, s *goquery.Selection) {
			ip := s.Find("td").Eq(1).Text()
			port := s.Find("td").Eq(2).Text()
			protocol := s.Find("td").Eq(5).Text()
			if ip != "" && port != "" {
				var in worker.InProxy
				in.Ip = ip
				in.Port = port
				in.Protocol = protocol
				inChan <- in
			}
		}(i, s)
	})
}

func TestAndAddRedis(inChan chan worker.InProxy) {
	fmt.Println(inChan)
	out := make(chan worker.Result)
	go func() {
		for {
			in := <-inChan
			fmt.Println(in)
			result := worker.Result{SuccessNum: 0}
			result.Ip = in.Ip
			result.Port = in.Port
			result.Protocol = in.Protocol
			go func(result worker.Result) {
				for i := 0; i < 10; i++ {
					err := do(result.Ip, result.Port, result.Protocol)
					if err != nil {
						//fmt.Println(err)
					} else {
						result.SuccessNum++
					}
				}

				out <- result
			}(result)
		}
	}()

	go func() {
		for {
			result := <-out
			save(&result)
		}
	}()
}

func do(ip string, port string, protocol string) error {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	p := fmt.Sprintf("%s://%s:%s", protocol, ip, port)
	proxy, err := url.Parse(p)

	if err != nil {
		return err
	}
	tr.Proxy = http.ProxyURL(proxy)
	client := &http.Client{
		Transport: tr,
		Timeout:   time.Second * 10, //超时时间
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse //禁止跳转
		},
	}
	r, err := http.NewRequest(http.MethodGet, "https://www.igxe.cn", nil)
	if err != nil {
		return err
	}

	resp, err := client.Do(r)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return errors.New(string(resp.StatusCode))
	}
	return nil
}

const SaveName = "proxies"

var n int

func save(result *worker.Result) {
	n++
	fmt.Print(n)
	fmt.Println(result)
	if result.SuccessNum >= 9 {
		client := redis.GetClient()
		var item worker.SaveItem
		item.Ip = result.Ip
		item.Protocol = strings.ToLower(result.Protocol)
		item.Port = result.Port
		key := fmt.Sprintf("%s://%s:%s", item.Protocol, item.Ip, item.Port)
		val := fmt.Sprintf("%s", "")
		client.HSet(SaveName, key, val)
	}
}

// 验证并且移除代理失败概率过大的代理服务器
func CheckAndRemoveFailed() {
	fmt.Println("start check failed")
	client := redis.GetClient()
	failed, err := client.HGetAll("failed_proxies").Result()

	if err != nil {
		fmt.Println(err)
	}

	for k, v := range failed {
		fNum, _ := strconv.Atoi(v)
		if fNum >= 15 {
			success, err := client.HGet("success_proxies", k).Result()
			if err != nil {
				fmt.Println(err)
				continue
			}
			sNum := 0

			if success == "" {
				sNum = 0
			} else {
				sNum, _ = strconv.Atoi(success)
			}

			rate := float64(fNum) / float64(sNum+fNum)

			if rate >= 0.5 {
				//失败率大于0.5删除这个proxy
				fmt.Printf("remove proxy: %s\n", k)
				client.HDel("proxies", k)
			}
		}
	}
}
